/* SACAR PRINTFS! */

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <ctype.h>
#include <sys/errno.h>
#include <sys/stat.h> /* Function to take information from a file */
#include <math.h>
#include <syslog.h>
#include <netdb.h>
#include <fcntl.h>
#include "../Headers/comm_tcp.h"
#include "../Headers/shared.h"


#define IP_DIR_LEN 15
#define SERVICE_LEN 5
#define SERVICE "12345"
#define IP "localhost"
#define QLEN 32

struct tcpCDT{
    char host[20]; 
    char service[6];
    int sock;
};

/* ------------------------------------------------------------------------- */
/* ---------------- Declaracion de Funciones auxiliares -------------------- */
/* ------------------------------------------------------------------------- */

static int connectsock (const char *host, const char *service, const char *transport );

static int connectTCP(const char *host, const char *service );

/* ------------------------------------------------------------------------- */
/* --------- Modulo de comunicacion entre los procesos --------------------- */
/* ------------------------------------------------------------------------- */

/* Inicia Sesion */

tcpADT
open_conn_tcp (hostTCP * phost)
{
    tcpADT conn;
    int sock;
    
    /* Valido la direccion IP */
    if (strlen (phost->host_dir) > IP_DIR_LEN || strlen (phost->service) > SERVICE_LEN)
        return NULL;
    
    if ((conn = malloc (sizeof (struct tcpCDT))) == NULL)
        return NULL;
        
    /* Me conecto al socket */
    if ((sock = connectTCP(phost->host_dir, phost->service )) == -1)
        return NULL;
    else
        conn->sock = sock;
               
    /* Guardo la informacion */
   
    strncpy (conn->host, phost->host_dir, strlen (phost->host_dir));
    strncpy (conn->service, phost->service, strlen (phost->service));
    
    return conn;
}

/* Envia Informacion */

int 
send_data_tcp (tcpADT conn, char * s_msg, int size)
{
    if (conn == NULL || s_msg == NULL || size < 0)
        return -1;
        
    return writen(conn->sock, s_msg, size);
}

/* Recibe Informacion */
int 
rcv_data_tcp (tcpADT conn, char * r_msg, int size)
{
    if (conn == NULL || r_msg == NULL)
        return -1;

        
    fcntl(conn->sock, F_SETFL, O_NONBLOCK);
    return readn(conn->sock, r_msg, size);
}

/* Cerrar Conexion */
int 
close_conn_tcp (tcpADT conn)
{  
    close (conn->sock);
    
    free (conn);
    
    return 1;
}

int
getFd_tcp(tcpADT conn)
{
    return conn->sock;
}

/* ------------------------------------------------------------------------- */
/* -------------- Implementacion de Funciones auxiliares ------------------- */
/* ------------------------------------------------------------------------- */


/* A la siguiente funcion, se le cambiaron los exit(1) por returns.
 * connectsock - allocate & connect a socket using TCP or UDP - Stevens
 */
 
static int
connectsock(const char *host, const char *service, const char *transport )
/*
 * Arguments:
 *      host      - name of host to which connection is desired
 *      service   - service associated with the desired port
 *      transport - name of transport protocol to use ("tcp" or "udp")
 */
{
        struct hostent  *phe;   /* pointer to host information entry    */
        struct servent  *pse;   /* pointer to service information entry */
        struct protoent *ppe;   /* pointer to protocol information entry*/
        struct sockaddr_in sin; /* an Internet endpoint address         */
        int     s, type;        /* socket descriptor and socket type    */


        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;

        /* Map service name to port number */
        if ( pse = getservbyname(service, transport) )
                sin.sin_port = pse->s_port;
        else if ( (sin.sin_port = htons((unsigned short)atoi(service))) == 0 ){
        
            fprintf(stdout, "can't get \"%s\" service entry\n", service);
            return -1;
        }

    /* Map host name to IP address, allowing for dotted decimal */
        if ( phe = gethostbyname(host) )
                memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
        else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE ) {
        
            fprintf(stdout, "can't get \"%s\" host entry\n", host);
            return -1;
        }

    /* Map transport protocol name to protocol number */
        if ( (ppe = getprotobyname(transport)) == 0){
                fprintf(stdout, "can't get \"%s\" protocol entry\n", transport);
                return -1;
        }

    /* Use protocol to choose a socket type */
        if (strcmp(transport, "udp") == 0)
                type = SOCK_DGRAM;
        else
                type = SOCK_STREAM;

    /* Allocate a socket */
        s = socket(PF_INET, type, ppe->p_proto);
        if (s < 0){
                fprintf(stdout, "can't create socket: %s\n", strerror(errno));
                return -1;
        }

    /* Connect the socket */
        if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
                fprintf(stdout, "can't connect to %s.%s: %s\n", host, service,
                        strerror(errno));
                return -1;
        }
        return s;
}

/* connectTCP - connect to a specified TCP service on a specified host - Stevens */
static int
connectTCP(const char *host, const char *service )
/*
 * Arguments:
 *      host    - name of host to which connection is desired
 *      service - service associated with the desired port
 */
{
        return connectsock( host, service, "tcp");
}
